// CanvasRenderer.ts
import { type IRendererOptions } from '../types'
import { type WorkerState } from './render.worker'

export class CanvasRenderer {
  private readonly options: IRendererOptions
  public canvas: HTMLCanvasElement
  private readonly worker: Worker
  private readonly offscreenCanvas: OffscreenCanvas
  private readonly bufferCanvas: OffscreenCanvas
  private scrollTop: number = 0 // 垂直滚动位置
  private scrollLeft: number = 0 // 水平滚动位置
  private isDragging = false
  private dragStartX = 0
  private scrollStartX = 0
  private isHoveringVerticalScrollBar: boolean = false // 鼠标是否悬停在垂直滚动条上
  private isHoveringHorizontalScrollBar: boolean = false // 鼠标是否悬停在水平滚动条上
  private readonly verticalScrollBarRect: { x: number, y: number, width: number, height: number } = {
    x: 0,
    y: 0,
    width: 10,
    height: 100
  } // 垂直滚动条区域

  private readonly horizontalScrollBarRect: { x: number, y: number, width: number, height: number } = {
    x: 0,
    y: 0,
    width: 100,
    height: 10
  } // 水平滚动条区域

  private readonly rowHeight: number = 32 // 默认行高
  private readonly columnWidth: number = 100 // 默认列宽
  private totalRows: number = 0 // 总行数
  private readonly totalColumns: number = 0 // 总列数
  private isResizing = false
  private resizeStartX = 0
  private resizeColumnIndex = -1
  private hoverRow: number = -1 // 当前悬停的行索引

  constructor (options: IRendererOptions) {
    this.options = options
    this.canvas = document.createElement('canvas')
    // 保存行高、列宽和总行数/列数
    this.rowHeight = options.rowHeight || 32
    this.columnWidth = options.columnWidth || 100
    this.totalRows = options.dataSource.length
    this.totalColumns = options.columns.length

    this.offscreenCanvas = new OffscreenCanvas(options.width, options.height)
    this.bufferCanvas = new OffscreenCanvas(options.width, options.height)

    // 初始化Worker
    this.worker = new Worker(new URL('./render.worker.ts', import.meta.url))

    this.worker.postMessage({
      type: 'init',
      bufferCanvas: this.bufferCanvas,
      options: {
        dpr: this.options.dpr
      }
    }, [this.bufferCanvas])

    // 设置canvas显示尺寸
    this.updateCanvasSize(options.width, options.height)

    this.requestRedraw()

    // 监听 bufferReady 事件
    this.worker.onmessage = (e) => {
      if (e.data.type === 'bufferReady') {
        const imageBitmap = e.data.buffer
        const mainCtx = this.canvas.getContext('2d')
        mainCtx.clearRect(0, 0, this.canvas.width, this.canvas.height)
        mainCtx.drawImage(imageBitmap, 0, 0)
        imageBitmap.close()

        this.drawVerticalScrollBar()
        this.drawHorizontalScrollBar()
      }
    }

    // 在 worker 初始化完成后添加鼠标事件监听器
    const addEventListeners = () => {
      // 添加鼠标事件监听器
      this.canvas.addEventListener('mousedown', this.handleMouseDown)
      this.canvas.addEventListener('mousemove', this.handleMouseMove)
      this.canvas.addEventListener('mouseup', this.handleMouseUp)
      this.canvas.addEventListener('wheel', this.handleWheel)
    }

    // 如果已经初始化完成，直接添加事件监听器
    if (this.worker && this.canvas) {
      addEventListeners()
    } else {
      // 否则等待初始化完成后再添加
      setTimeout(addEventListeners, 100)
    }

    this.setupEvents()
  }

  getWorkerOptions (): IRendererOptions {
    return {
      ...this.options,
      container: null,
      columns: this.options.columns.map((item) => {
        return {
          ...item,
          render: null
        }
      }),
      getTotalRows: null,
      getRowData: null,
    }
  }

  getWorkerState (): Omit<WorkerState, 'bufferCtx' | 'options'> {
    return {
      scrollTop: this.scrollTop,
      scrollLeft: this.scrollLeft,
      totalColumnWidth: this.getTotalColumnWidth(),
      selectedRowIndices: this.selectedRowIndices ?? [],
      hoverRow: this.hoverRow,
      options: this.getWorkerOptions()
    }
  }

  private setupEvents (): void {
    // 添加点击事件监听器
    this.options.container.addEventListener('click', (event) => {
      const rect = this.options.container.getBoundingClientRect()
      const x = event.clientX - rect.left
      const y = event.clientY - rect.top

      // 计算点击的行索引
      const rowIndex = Math.floor(y / this.rowHeight)

      // 如果启用了 selectionMode 且点击位置在左侧 40px 区域内
      if (this.options.selectionMode && x >= 0 && x <= 40) {
        // 判断是否点击的是表头区域
        if (rowIndex === 0 && y < this.rowHeight) {
          // 表头勾选框被点击，触发全选/取消全选
          if (this.selectedRowIndices.length === this.options.dataSource?.length) {
            this.selectedRowIndices = []
          } else {
            this.selectedRowIndices = this.options.dataSource?.map((_, i) => i) || []
          }
          this.dispatchEvent(new CustomEvent('selectionChange', { detail: this.selectedRowIndices }))
        } else {
          // 内容行勾选框被点击，切换当前行选择状态
          // TODO 勾选功能逻辑还未实现，只处理了渲染
          // this.selectRow(rowIndex)
        }
      }
    })

    // 新增：监听鼠标移动事件以更新悬停状态
    this.options.container.addEventListener('mousemove', (event) => {
      const rect = this.options.container.getBoundingClientRect()
      const y = event.clientY - rect.top

      // 计算可见区域起始行索引和当前悬停行
      const scrollTop = this.scrollLeft // 修正：从正确的属性获取 scrollTop 值
      const hoverVisibleRowCount = Math.floor(rect.height / this.rowHeight)
      const startRow = Math.floor(scrollTop / this.rowHeight)
      const rowIndex = startRow + Math.floor((y - scrollTop % this.rowHeight) / this.rowHeight)

      // 更新悬停行
      if (y < hoverVisibleRowCount * this.rowHeight && rowIndex >= 0 && rowIndex < this.totalRows) {
        this.hoverRow = rowIndex
      } else {
        this.hoverRow = -1
      }

      // 触发重绘
      this.requestRedraw()
    })

    // 添加 mouseleave 事件以清除悬停状态
    this.options.container.addEventListener('mouseleave', () => {
      this.hoverRow = -1
      this.requestRedraw()
    })
  }

  // 新增 updateData 方法
  public updateData (data: IBaseData[]): void {
    this.totalRows = data.length // 更新总行数
    this.requestRedraw()
  }

  // ✨ 新增方法：绘制垂直滚动条
  private drawVerticalScrollBar (): void {
    const ctx = this.canvas.getContext('2d')
    const canvasHeight = this.canvas.height

    // 计算可视区域行数
    const visibleRowCount = Math.floor(canvasHeight / this.rowHeight)
    // 滑块高度 = (可视区域行数 / 总行数) × 画面高度
    const scrollBarHeight = Math.max(20, (visibleRowCount / this.totalRows) * canvasHeight)
    const maxScrollTop = (this.totalRows - visibleRowCount) * this.rowHeight
    const scrollBarMaxY = canvasHeight - scrollBarHeight
    // 滑块位置 = scrollTop / (总高度 - 画面高度) × (画面高度 - 滑块高度)
    const scrollBarY = (this.scrollTop / (maxScrollTop || 1)) * scrollBarMaxY

    // 更新滚动条区域
    this.verticalScrollBarRect.y = scrollBarY
    this.verticalScrollBarRect.height = scrollBarHeight

    // 绘制滚动条背景
    ctx.fillStyle = '#f1f1f1'
    ctx.fillRect(
      this.canvas.width - this.verticalScrollBarRect.width,
      0,
      this.verticalScrollBarRect.width,
      canvasHeight
    )

    // 绘制垂直滚动条滑块
    ctx.fillStyle = this.isHoveringVerticalScrollBar ? '#555' : '#888'
    ctx.fillRect(
      this.canvas.width - this.verticalScrollBarRect.width,
      this.verticalScrollBarRect.y,
      this.verticalScrollBarRect.width,
      this.verticalScrollBarRect.height
    )
  }

  // ✨ 新增方法：绘制水平滚动条
  private drawHorizontalScrollBar (): void {
    const ctx = this.canvas.getContext('2d')
    const canvasWidth = this.canvas.width

    // 计算总列宽
    const totalColumnWidth = this.getTotalColumnWidth()
    const hasHorizontalScroll = totalColumnWidth > canvasWidth

    if (!hasHorizontalScroll) return

    // 滑块宽度 = (可视区域宽度 / 总列宽) × 画面宽度
    const scrollBarWidth = Math.max(20, (canvasWidth / totalColumnWidth) * canvasWidth)
    const maxScrollLeft = totalColumnWidth - canvasWidth
    const scrollBarMaxX = canvasWidth - scrollBarWidth
    const scrollBarX = (this.scrollLeft / (maxScrollLeft || 1)) * scrollBarMaxX

    // 更新滚动条区域
    this.horizontalScrollBarRect.x = scrollBarX
    this.horizontalScrollBarRect.width = scrollBarWidth

    // 绘制滚动条背景
    ctx.fillStyle = '#f1f1f1'
    ctx.fillRect(
      0,
      this.canvas.height - this.horizontalScrollBarRect.height,
      canvasWidth,
      this.horizontalScrollBarRect.height
    )

    // 绘制水平滚动条滑块
    ctx.fillStyle = this.isHoveringHorizontalScrollBar ? '#555' : '#888'
    ctx.fillRect(
      this.horizontalScrollBarRect.x,
      this.canvas.height - this.horizontalScrollBarRect.height,
      this.horizontalScrollBarRect.width,
      this.horizontalScrollBarRect.height
    )
  }

  // ✨ 新增方法：计算总列宽（支持动态列宽）
  private getTotalColumnWidth (): number {
    return this.options.columns.reduce((total, col) => total + (col.width || 120), 0)
  }

  private readonly handleMouseDown = (e: MouseEvent): void => {
    const rect = this.canvas.getBoundingClientRect()
    const mouseX = e.clientX - rect.left
    const mouseY = e.clientY - rect.top

    // 判断是否点击了垂直滚动条
    const inVertical = (
      mouseX >= this.canvas.width - this.verticalScrollBarRect.width &&
      mouseY >= this.verticalScrollBarRect.y &&
      mouseY <= this.verticalScrollBarRect.y + this.verticalScrollBarRect.height
    )

    // 判断是否点击了水平滚动条
    const inHorizontal = (
      mouseY >= this.canvas.height - this.horizontalScrollBarRect.height &&
      mouseX >= this.horizontalScrollBarRect.x &&
      mouseX <= this.horizontalScrollBarRect.x + this.horizontalScrollBarRect.width
    )

    // 检查是否点击了列边界
    let clickedColumnResize = false
    for (let i = 0; i < this.options.columns.length; i++) {
      const colWidth = this.options.columns[i].width || 120
      const colX = this.getColumnXPosition(i)
      const resizeAreaStart = colX + colWidth - 5
      const resizeAreaEnd = colX + colWidth + 5

      if (mouseX >= resizeAreaStart && mouseX <= resizeAreaEnd) {
        this.isResizing = true
        this.resizeStartX = e.clientX
        this.resizeColumnIndex = i
        clickedColumnResize = true
        break
      }
    }

    if (!inVertical && !inHorizontal && !clickedColumnResize) return

    this.isDragging = true
    this.dragStartX = e.clientX
    this.scrollStartX = inHorizontal ? this.scrollLeft : 0

    // 使用 document 监听全局 mousemove 和 mouseup 以增强拖动体验
    document.addEventListener('mousemove', this.handleGlobalMouseMove)
    document.addEventListener('mouseup', this.handleGlobalMouseUp)

    e.preventDefault() // 防止选中文本干扰
  }

  private readonly handleGlobalMouseMove = (e: MouseEvent): void => {
    if (!this.isDragging && !this.isResizing) return

    const rect = this.canvas.getBoundingClientRect()
    const mouseX = e.clientX - rect.left
    const mouseY = e.clientY - rect.top

    // 始终使用初始点击时的状态来决定滚动方向
    const isHorizontalDrag = this.isHoveringHorizontalScrollBar
    const isVerticalDrag = this.isHoveringVerticalScrollBar

    // 处理垂直拖动
    if (isVerticalDrag) {
      const canvasHeight = this.canvas.height
      const visibleRowCount = Math.floor(canvasHeight / this.rowHeight)
      const maxScrollTop = (this.totalRows - visibleRowCount) * this.rowHeight

      if (maxScrollTop > 0) {
        const scrollBarHeight = this.verticalScrollBarRect.height
        const scrollBarMaxY = canvasHeight - scrollBarHeight
        const newY = Math.max(0, Math.min(mouseY - scrollBarHeight / 2, scrollBarMaxY))

        this.verticalScrollBarRect.y = newY
        this.scrollTop = Math.round((newY / scrollBarMaxY) * maxScrollTop)

        this.requestRedraw()
      }
    }

    // 处理水平拖动
    if (isHorizontalDrag) {
      const canvasWidth = this.canvas.width
      const totalColumnWidth = this.getTotalColumnWidth()
      const maxScrollLeft = totalColumnWidth - canvasWidth

      if (maxScrollLeft > 0) {
        const scrollBarWidth = this.horizontalScrollBarRect.width
        const scrollBarMaxX = canvasWidth - scrollBarWidth
        const newX = Math.max(0, Math.min(mouseX - scrollBarWidth / 2, scrollBarMaxX))

        this.horizontalScrollBarRect.x = newX
        this.scrollLeft = Math.round((newX / scrollBarMaxX) * maxScrollLeft)

        // this.requestRedraw()
      }
    }

    // 处理列宽调整
    if (this.isResizing && this.resizeColumnIndex >= 0) {
      const deltaX = e.clientX - this.resizeStartX
      const column = this.options.columns[this.resizeColumnIndex]
      const newWidth = Math.max(50, (column.width || 120) + deltaX) // 最小宽度限制为50

      // 更新列宽
      column.width = newWidth
      this.resizeStartX = e.clientX
    }

    this.requestRedraw()
  }

  private readonly handleGlobalMouseUp = (): void => {
    this.isDragging = false
    this.isResizing = false
    this.isHoveringVerticalScrollBar = false
    this.isHoveringHorizontalScrollBar = false
    this.resizeColumnIndex = -1

    // 移除全局事件监听器
    document.removeEventListener('mousemove', this.handleGlobalMouseMove)
    document.removeEventListener('mouseup', this.handleGlobalMouseUp)

    // 恢复画布默认光标样式
    this.canvas.style.cursor = 'inherit'

    this.requestRedraw()
  }

  private readonly handleMouseMove = (e: MouseEvent): void => {
    const rect = this.canvas.getBoundingClientRect()
    const mouseX = e.clientX - rect.left
    const mouseY = e.clientY - rect.top

    // 持续检测是否悬停在滚动条或列边界上（仅在非拖动状态下更新）
    if (!this.isDragging && !this.isResizing) {
      const wasHoveringVertical = this.isHoveringVerticalScrollBar
      const wasHoveringHorizontal = this.isHoveringHorizontalScrollBar

      const canvasHeight = this.canvas.height
      const visibleRowCount = Math.floor(canvasHeight / this.rowHeight)
      const hasVerticalScroll = this.totalRows > visibleRowCount

      const canvasWidth = this.canvas.width
      const totalColumnWidth = this.getTotalColumnWidth()
      const hasHorizontalScroll = totalColumnWidth > canvasWidth

      // 悬停检测部分新增：使用新的变量名
      const hoverVisibleRowCount = Math.floor(canvasHeight / this.rowHeight)
      // 计算可见区域起始行索引
      const startRow = Math.floor(this.scrollTop / this.rowHeight)
      // 计算鼠标所在的行索引
      const rowIndex = startRow + Math.floor((mouseY - this.scrollTop % this.rowHeight) / this.rowHeight)

      // 更新悬停行
      if (mouseY < hoverVisibleRowCount * this.rowHeight && rowIndex >= 0 && rowIndex < this.totalRows) {
        this.hoverRow = rowIndex
      } else {
        this.hoverRow = -1
      }

      this.isHoveringVerticalScrollBar = hasVerticalScroll && (
        mouseX >= this.canvas.width - this.verticalScrollBarRect.width &&
        mouseY >= this.verticalScrollBarRect.y &&
        mouseY <= this.verticalScrollBarRect.y + this.verticalScrollBarRect.height
      )

      this.isHoveringHorizontalScrollBar = hasHorizontalScroll && (
        mouseY >= this.canvas.height - this.horizontalScrollBarRect.height &&
        mouseX >= this.horizontalScrollBarRect.x &&
        mouseX <= this.horizontalScrollBarRect.x + this.horizontalScrollBarRect.width
      )

      // 检测是否悬停在列边界
      let isHoveringColumnResize = false
      for (let i = 0; i < this.options.columns.length; i++) {
        const colWidth = this.options.columns[i].width || 120
        const colX = this.getColumnXPosition(i)
        const resizeAreaStart = colX + colWidth - 5 // 列右侧5px内为调整区域
        const resizeAreaEnd = colX + colWidth + 5

        if (mouseX >= resizeAreaStart && mouseX <= resizeAreaEnd) {
          isHoveringColumnResize = true
          this.resizeColumnIndex = i
          break
        }
      }

      // 新增：检测是否悬停在表格行上
      this.canvas.style.cursor = 'inherit'
      // 只要悬停状态发生变化就触发重绘
      if (
        this.isHoveringVerticalScrollBar !== wasHoveringVertical ||
        this.isHoveringHorizontalScrollBar !== wasHoveringHorizontal ||
        isHoveringColumnResize
      ) {
        this.requestRedraw()
        // 设置列边界悬停时的光标样式
        if (isHoveringColumnResize) {
          this.canvas.style.cursor = 'col-resize'
        }
      }
    }
  }

  private readonly handleMouseUp = (e: MouseEvent): void => {
    this.isDragging = false
    this.isResizing = false
    this.isHoveringVerticalScrollBar = false
    this.isHoveringHorizontalScrollBar = false
    this.resizeColumnIndex = -1

    // 恢复画布默认光标样式
    this.canvas.style.cursor = 'inherit'

    // 移除全局事件监听器
    document.removeEventListener('mousemove', this.handleGlobalMouseMove)
    document.removeEventListener('mouseup', this.handleGlobalMouseUp)

    this.requestRedraw()
  }

  private readonly handleWheel = (e: WheelEvent): void => {
    const canvasHeight = this.canvas.height
    const canvasWidth = this.canvas.width
    const visibleRowCount = Math.floor(canvasHeight / this.rowHeight)
    const visibleColumnCount = Math.floor(canvasWidth / this.columnWidth)

    const hasVerticalScroll = this.totalRows > visibleRowCount
    const hasHorizontalScroll = this.totalColumns > visibleColumnCount

    // 判断滚轮方向并更新滚动位置
    if (e.shiftKey && hasHorizontalScroll) {
      // 横向滚动
      const maxScrollLeft = (this.totalColumns - visibleColumnCount) * this.columnWidth
      const scrollBarMaxX = canvasWidth - this.horizontalScrollBarRect.width

      this.scrollLeft += e.deltaX > 0 || e.deltaY > 0 ? this.columnWidth : -this.columnWidth
      this.scrollLeft = Math.max(0, Math.min(this.scrollLeft, maxScrollLeft))

      // 更新滚动条位置
      this.horizontalScrollBarRect.x = (this.scrollLeft / (maxScrollLeft || 1)) * scrollBarMaxX
    } else if (hasVerticalScroll) {
      // 纵向滚动
      const maxScrollTop = (this.totalRows - visibleRowCount) * this.rowHeight
      const scrollBarMaxY = canvasHeight - this.verticalScrollBarRect.height

      this.scrollTop += e.deltaY > 0 ? this.rowHeight : -this.rowHeight
      this.scrollTop = Math.max(0, Math.min(this.scrollTop, maxScrollTop))

      // 更新滚动条位置
      const scrollBarY = (this.scrollTop / (maxScrollTop || 1)) * scrollBarMaxY
      this.verticalScrollBarRect.y = scrollBarY
    }

    this.requestRedraw()
  }

  // 获取指定列的X轴起始位置
  private getColumnXPosition (columnIndex: number): number {
    if (columnIndex <= 0) return 0

    // 计算前面所有列的总宽度
    return this.options.columns
      .slice(0, columnIndex)
      .reduce((total, col) => total + (col.width || 120), 0)
  }

  // 检查鼠标是否悬停在列的调整区域
  private isMouseOverColumnResizeArea (mouseX: number, columnIndex: number): boolean {
    const colX = this.getColumnXPosition(columnIndex)
    const colWidth = this.options.columns[columnIndex].width || 120
    const resizeAreaStart = colX + colWidth - 5 // 列右侧5px内为调整区域
    const resizeAreaEnd = colX + colWidth + 5

    return mouseX >= resizeAreaStart && mouseX <= resizeAreaEnd
  }

  private updateCanvasSize (width: number, height: number): void {
    this.canvas.style.width = `${width}px`
    this.canvas.style.height = `${height}px`
    this.canvas.width = width
    this.canvas.height = height
  }

  public requestRedraw (): void {
    this.worker.postMessage({
      type: 'render',
      workerState: this.getWorkerState()
    })
  }

  public destroy (): void {
    this.worker.terminate()
    this.canvas.removeEventListener('wheel', this.handleWheel)
    this.canvas.remove()
  }
}

export type { IRendererOptions } from '../types'
